﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Diagnostics;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using avlib_wrapper;

namespace dmm
{
    public class DeviceInfo
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public DeviceInfo(int id, string name)
        {
            Id = id; Name = name;
        }
    }

    public interface RecorderObserver
    {
        void onRecordStart();
        void onRecordError(int error, string errtext);
        void onRecordProgress(int frame, int nsample);
        void onRecordEnd();
    }
    /*interface RecorderController
    {
        virtual void 
    }*/
    public class Recorder : RecorderObserver
    {

        List<RecorderObserver> observers = new List<RecorderObserver>();
        private AVRecordSetting setting = new AVRecordSetting();

        AVRecorderWrapper native_recorder = null;

        private ObservableCollection<DeviceInfo> vDevs = new ObservableCollection<DeviceInfo>();
        private ObservableCollection<DeviceInfo> aDevs = new ObservableCollection<DeviceInfo>();
        public ObservableCollection<DeviceInfo> VideoDevices
        {
            get { return vDevs; }
        }

        public Window Win { get; set; } //used to dispatch action
        private Video video;

        //should only be called when record end
        public Video RecordedVideo
        {
            get
            {
                return video;
            }
        }
        public ObservableCollection<DeviceInfo> AudioDevices
        {
            get { return aDevs; }
        }

        public AVRecordSetting Setting
        {
            get { return setting; }
        }

        public Double Duration
        {
            get 
            {
                double seconds =  native_recorder.get_duration();
                video.Duration = seconds;
                return seconds; 
            }
        }
        public bool IsRrcording
        {
            get { return native_recorder.is_recording(); }
        }
        public Recorder()
        {
            native_recorder = new AVRecorderWrapper();
            native_recorder.Init(EnumVideoDeviceCallback, EnumAudioDeviceCallback);
        }


         ~Recorder()
        {
        }

        [StructLayout(LayoutKind.Sequential)]
        struct RECT
        {
            public int left, top, right, bottom;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int X;
            public int Y;

            public POINT(int x, int y)
            {
                this.X = x;
                this.Y = y;
            }

            public static implicit operator System.Drawing.Point(POINT p)
            {
                return new System.Drawing.Point(p.X, p.Y);
            }

            public static implicit operator POINT(System.Drawing.Point p)
            {
                return new POINT(p.X, p.Y);
            }
        }


        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool GetCursorPos(out POINT lpPoint);
        [DllImport("User32.dll")]
        static extern IntPtr WindowFromPoint(int x, int y);
        [DllImport("User32.dll")]
        static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
        [DllImport("User32.dll")]
        static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint);


        public void capture_current_win_rect(int left, int top, int width, int height)
        {
            if (IsRrcording) return;
            try
            {
                RECT rc = new RECT();

                rc.left = left;
                rc.top = top;
                rc.right = left + width;
                rc.bottom = top + height;

                POINT pt = new POINT();
                bool res = GetCursorPos(out pt);
                if (!res)
                {
                    Debug.Write("GetCursorPos(ref pt) error\n");
                }
                else
                {
                    Debug.Write("PoisiotnX:" + (int)pt.X + ",PositionY:" + (int)pt.Y + "\n");
                }
                IntPtr hwnd = WindowFromPoint((int)pt.X, (int)pt.Y);
                //GetClientRect(hwnd, out rc);
                pt.X = 0; pt.Y = 0;
                ClientToScreen(hwnd, ref pt);

                rc.left = (int)pt.X + left;
                rc.top = (int)pt.Y + top;
                rc.right = rc.left + width;
                rc.bottom = rc.top + height;

                Setting.VideoDevice = 0;
                Setting.AudioDevice = 0;
                Setting.FPS = 25;
                Setting.Left = rc.left;
                Setting.Top = rc.top;
                Setting.Width = width;
                Setting.Height = height;

                /*string avi_path = @"g:\tv\record\";
                string thumb_path = @"g:\tv\record\thumbnail\";
                setting.VideoFile = avi_path+ "canvas_"  + dba.get_id()+ ".avi";
                setting.ThumbnailFile = thumb_path + "canvas_" + dba.get_id() + ".avi.bmp";*/

                start();

            }
            catch (Exception e)
            {
                Debug.Write(e);
            }
        }


        public void start()
        {

            Setting.VideoFile = Options.newUniqueFile(Options.Instance.VideoOutPath, DateTime.Today.ToString("yyyy-MM-dd"), "avi");
            Setting.ThumbnailFile = Options.newUniqueFile(Options.Instance.ThumbnailOutPath, DateTime.Today.ToString("yyyy-MM-dd"), "png");


            video = new Video();
            video.Name = "RecordVideo";
            video.Path = Setting.VideoFile;
            video.Thumbnail = Setting.ThumbnailFile;
            video.Width = Setting.Width;
            video.Height = Setting.Height;

            video.CreateDate = System.DateTime.Now.ToString();
            video.Duration = 0;
            //current_video.Desc = "";
            //v.Url = strUrl;
            video.Srctype = VideoSourceType.VideoFromRecord;

            onRecordStart();

            native_recorder.set_rect(Setting.Left, Setting.Top, Setting.Width, Setting.Height);
            native_recorder.use_record_device(Setting.VideoDevice, Setting.AudioDevice);
            native_recorder.set_fps(Setting.FPS);
            native_recorder.set_record_callback(onRecordProgress, onRecordError, onRecordEnd);
            native_recorder.start(Setting.VideoFile, Setting.ThumbnailFile, Setting.PreviewWnd);
            //if (Setting.PreviewWnd != 0) native_recorder.set_preview_window(Setting.PreviewWnd);

        }

        public void stop()
        {
            native_recorder.stop();

        }

        public void SetPreviewWindow(IntPtr win)
        {
            new Thread(new ThreadStart(delegate()
            {
                native_recorder.set_preview_window(win);
            })).Start();
        }

        private void EnumAudioDeviceCallback(int id, string name)
        {
            aDevs.Add(new DeviceInfo(id, name));
        }

        private void EnumVideoDeviceCallback(int id, string name)
        {
            vDevs.Add(new DeviceInfo(id, name));
        }

        public void QueryDimension(int vid, ArrayList arr_dim)
        {
            native_recorder.query_dimension(vid, arr_dim);
        }

        public void register_observer(RecorderObserver obs)
        {
            observers.Add(obs);
        }

        public void unregister_observer(RecorderObserver obs)
        {
            observers.Remove(obs);
        }


        public void onRecordStart()
        {

            Win.Dispatcher.BeginInvoke(new Action(() =>
            {
                try
                {
                    foreach (RecorderObserver obs in observers)
                    {
                        obs.onRecordStart();
                    }
                }
                catch (Exception e)
                {
                    Debug.Write(e);
                }
            }));
        }
        public void onRecordError(int error, string errtext)
        {

            Win.Dispatcher.BeginInvoke(new Action(() =>
            {
                try
                {
                    foreach (RecorderObserver obs in observers)
                    {
                        obs.onRecordError(error, errtext);
                    }
                }
                catch (Exception e)
                {
                    Debug.Write(e);
                }
            }));
        }
        public void onRecordProgress(int frame, int nsample)
        {

            Win.Dispatcher.BeginInvoke(new Action(() =>
            {
                try
                {
                    foreach (RecorderObserver obs in observers)
                    {
                        obs.onRecordProgress(frame, nsample);
                    }
                }
                catch (Exception e)
                {
                    Debug.Write(e);
                }
            }));
        }
        public void onRecordEnd()
        {

            Win.Dispatcher.BeginInvoke(new Action(() =>
             {
                 try
                 {
                     foreach (RecorderObserver obs in observers)
                     {
                         obs.onRecordEnd();
                     }
                 }
                 catch (Exception e)
                 {
                     Debug.Write(e);
                 }
             }));
        }
    }

}
